/**
  ******************************************************************************
  * @file    IIC_Simulate_Config.c
  * @brief   I2C总线模拟驱动（软件实现）
  * @author  18408107475@163.com
  * @version V1.0
  * @date    2025-6-6
  * @note    适用于STM32F1系列，通过GPIO模拟I2C时序
  *          移植说明：
  *          1. 修改IIC_SCL_PORT/PIN和IIC_SDA_PORT/PIN为实际使用的引脚
  *          2. 调整Delay_us()函数为实际的微秒延时函数
  ******************************************************************************
  */

#include "IIC_Simulate_Config.h"

/**
  * @brief  I2C时钟线(SCL)硬件配置
  * @param  None
  * @retval None
  * @note   硬件要求：
  *         1. 必须使用开漏输出模式(GPIO_Mode_Out_OD)
  *         2. 必须外接4.7KΩ上拉电阻至3.3V
  *         3. 建议GPIO速度配置为50MHz以减少信号边沿时间
  *         
  *         设计规范：
  *         1. 与SDA线使用相同GPIO组时可共享时钟使能
  *         2. 实际引脚需通过IIC_SCL_PORT/PIN宏定义
  *         
  * @warning 
  *         1. 未启用GPIO时钟将导致配置失效
  *         2. 错误配置为推挽输出可能损坏设备
  *         
  * @code
  *     // 宏定义示例（应在头文件中定义）
  *     #define IIC_SCL_PORT  GPIOB
  *     #define IIC_SCL_PIN   GPIO_Pin_6
  * @endcode
  */
static void IIC_SCL_Config(void)
{
    /* 使能GPIO时钟（多次调用不会重复使能） */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    /* 开漏输出配置结构体初始化 */
    GPIO_InitTypeDef GPIO_InitStructure = {
        .GPIO_Mode  = GPIO_Mode_Out_OD,  // 开漏输出（必须）
        .GPIO_Pin   = IIC_SCL_PIN,       // 引脚号通过宏定义
        .GPIO_Speed = GPIO_Speed_50MHz   // 高速模式减少信号延迟
    };

    /* 应用配置 */
    GPIO_Init(IIC_SCL_PORT, &GPIO_InitStructure);
    
    /* 初始状态置高（释放总线） */
    GPIO_SetBits(IIC_SCL_PORT, IIC_SCL_PIN);
}

/**
  * @brief  动态配置I2C数据线(SDA)工作模式
  * @param  mode: 引脚工作模式
  *         @arg GPIO_Mode_Out_OD: 开漏输出模式（主机发送时使用）
  *         @arg GPIO_Mode_IN_FLOATING: 浮空输入模式（主机接收时使用）
  * @retval None
  * @note   关键设计要点：
  *         1. 输出模式必须为开漏输出(OD)，禁止使用推挽输出(PP)
  *         2. 输入模式必须为浮空输入，禁止使用上拉/下拉输入
  *         3. 模式切换频率应 <100kHz（标准I2C速率）
  *         
  *         电气特性：
  *         1. 输出低电平 <0.4V @3mA sink current
  *         2. 输入高电平 >0.7*VDD
  *         3. 引脚转换速率 <50ns（对应50MHz配置）
  *         
  * @warning 
  *         1. 模式切换后需至少1us的稳定时间
  *         2. 同一总线上多个主机不可同时配置为输出模式
  *         
  * @usage   典型调用场景：
  *         @code
  *         // 发送起始信号前配置
  *         IIC_SDA_Config(GPIO_Mode_Out_OD);
  *         
  *         // 接收数据前配置  
  *         IIC_SDA_Config(GPIO_Mode_IN_FLOATING);
  *         @endcode
  */
static void IIC_SDA_Config(GPIOMode_TypeDef mode)
{
    /* 总线安全检测（可选调试功能） */
    #ifdef I2C_SAFETY_CHECK
    if(mode == GPIO_Mode_Out_PP) {
		USART1_Printf("Please Use GPIO_Mode_Out_OD");
    }
    #endif

    /* 使能GPIO时钟（幂等操作） */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    /* 动态重配置结构体初始化 */
    GPIO_InitTypeDef GPIO_InitStructure = {
        .GPIO_Mode  = mode,               // 模式由调用方指定
        .GPIO_Pin   = IIC_SDA_PIN,        // 使用预定义的SDA引脚
        .GPIO_Speed = GPIO_Speed_50MHz    // 高速模式确保信号完整性
    };

    /* 应用硬件配置 */
    GPIO_Init(IIC_SDA_PORT, &GPIO_InitStructure);
    
    /* 模式切换后延时（确保硬件稳定） */
    if(mode == GPIO_Mode_IN_FLOATING) {
        Delay_us(1); // 输入模式建立时间
    }
}

/**
  * @brief  I2C总线初始化
  * @param  无
  * @retval 无
  * @note   初始化后总线处于空闲状态(SCL和SDA都为高电平)
  */
void IIC_Init(void)
{
    /* 硬件接口初始化 */
    IIC_SCL_Config();                      // SCL强制配置为开漏输出
    IIC_SDA_Config(GPIO_Mode_Out_OD);      // SDA默认配置为开漏输出
    
    /* 总线进入空闲状态 */
    SDA_SET(1);                            // 释放SDA线（高电平）
    SCL_SET(1);                            // 释放SCL线（高电平）
    IIC_Delay();                           // 维持空闲状态≥100us
}

/**
  * @brief  产生I2C起始信号
  * @param  无
  * @retval 无
  * @note   起始条件：SCL高电平时，SDA从高变低
  *         时序要求：
  *         1. SCL高电平期间SDA下降沿
  *         2. 起始条件建立时间(t_HD;STA) ≥4.0us
  *         3. 起始条件保持时间(t_SU;STA) ≥4.7us
  */
void IIC_Start(void)
{
    /* 确保SDA为输出模式（主机控制） */
    IIC_SDA_Config(GPIO_Mode_Out_OD);  
    
    /* 步骤1：建立起始前状态（SCL和SDA都为高） */
    SDA_SET(1);          // 释放SDA线
    SCL_SET(1);          // 释放SCL线
    IIC_Delay();         // 延时≥4.7us（满足t_SU;STA）
    
    /* 步骤2：产生起始条件（SCL高时SDA变低） */
    SDA_SET(0);          // START信号下降沿
    IIC_Delay();         // 延时≥4.0us（满足t_HD;STA）
    
    /* 步骤3：准备数据传输（拉低SCL） */
    SCL_SET(0);          // 时钟线拉低
    IIC_Delay();         // 保持时间
}

/**
  * @brief  产生I2C停止信号  
  * @param  无
  * @retval 无
  * @note   停止条件：SCL高电平时，SDA从低变高
  *         时序要求：
  *         1. SCL上升沿前SDA建立时间(t_SU;STO) ≥4.0us  
  *         2. 停止条件保持时间(t_BUF) ≥4.7us
  *         3. 总线空闲前恢复时间(t_IDLE) ≥1.3us
  * @warning 
  *         1. 必须确保SCL最后为高电平
  *         2. 停止信号后总线必须保持空闲状态
  */
void IIC_Stop(void)
{
    /* 强制SDA为输出模式（主机控制） */  
    IIC_SDA_Config(GPIO_Mode_Out_OD);
    
    /* 步骤1：建立停止前状态（SCL=0, SDA=0） */
    SDA_SET(0);          // 确保SDA为低
    SCL_SET(0);          // 确保SCL为低  
    IIC_Delay();         // 延时≥4.0us（满足t_SU;STO）
    
    /* 步骤2：产生停止条件（SCL高时SDA变高） */
    SCL_SET(1);          // 先释放SCL
    IIC_Delay();         // 保持≥4.7us（满足t_BUF）
    SDA_SET(1);          // STOP信号上升沿
    IIC_Delay();         // 保持≥1.3us（满足t_IDLE）
}

/**
  * @brief  通过I2C总线发送单字节数据
  * @param  byte: 待发送数据（0x00-0xFF）
  * @retval None
  * @note   传输特性：
  *         1. MSB（高位优先）传输协议
  *         2. 数据在SCL低电平时变化，高电平时稳定
  *         3. 每个bit传输包含完整时钟脉冲
  *         
  *         时序要求（标准模式100kHz）：
  *         参数      | 最小值 | 典型值
  *         ----------|--------|--------
  *         t_HD;DAT  |  0     |  4.0us (数据保持时间)
  *         t_SU;DAT  |  0.25  |  4.7us (数据建立时间)
  *         t_LOW     |  4.7   |  5.0us (时钟低电平)
  *         t_HIGH    |  4.0   |  4.5us (时钟高电平)
  *         
  * @warning 
  *         1. 必须先调用IIC_Start()启动传输
  *         2. 发送后必须调用IIC_Wait_Ack()等待应答
  */
void IIC_Send_Byte(u8 byte)
{
    /* 强制SDA为输出模式（主机控制） */
    IIC_SDA_Config(GPIO_Mode_Out_OD);
    
    /* 确保时钟线起始状态为低 */
    SCL_SET(0);
    IIC_Delay();  // t_HD;DAT ≥0us
    
    /* 逐位传输（MSB First） */
    for(u8 i = 0; i < 8; i++) {
        /* 数据准备阶段（SCL=0时变化） */
        (byte & 0x80) ? SDA_SET(1) : SDA_SET(0);
        byte <<= 1;
        IIC_Delay();  // t_SU;DAT ≥4.7us
        
        /* 时钟上升沿（数据锁存） */
        SCL_SET(1);
        IIC_Delay();  // t_HIGH ≥4.0us
        
        /* 时钟下降沿（准备下一位） */
        SCL_SET(0);
        IIC_Delay();  // t_LOW ≥4.7us
    }
}

/**
  * @brief  等待并检测从机应答信号
  * @param  None
  * @retval bool 
  *         - true:  收到有效应答(ACK)
  *         - false: 未收到应答(NACK)或超时
  * @note   协议要求：
  *         1. 第9个时钟周期检测SDA电平
  *         2. 从机必须在SCL高电平期间拉低SDA
  *         3. 超时时间必须大于从机最大响应时间
  *         
  *         电气特性：
  *         参数      | 条件              | 典型值
  *         ----------|-------------------|--------
  *         t_ACK     | 从机响应时间      | <50us
  *         t_TIMEOUT | 推荐超时阈值      | 1ms
  *         V_IL      | 低电平识别阈值    | <0.3VDD
  *         
  * @warning 
  *         1. 必须在前8个数据位传输后调用
  *         2. 超时后会自动发送STOP信号
  *         3. 从机忙状态可能导致超时
  */
bool IIC_Wait_Ack(void)
{
    /* 超时计数器（基于系统时钟） */
    volatile u32 timeout = SystemCoreClock / 1000;  // 1ms基准
    
    /* 配置SDA为输入（释放总线控制权） */
    IIC_SDA_Config(GPIO_Mode_IN_FLOATING);
    
    /* 产生第9个时钟脉冲 */
    SCL_SET(0);
    IIC_Delay();                     // t_LOW ≥4.7us
    SCL_SET(1);
    IIC_Delay();                     // t_HIGH ≥4.0us
    
    /* 检测应答信号（带超时保护） */
    while(SDA_READ) {                  // 检测低电平应答
        if(timeout-- == 0)           // 超时判定
        {
            IIC_Stop();              // 异常终止传输
            IIC_Delay();
            
            #ifdef I2C_DEBUG
            USART1_Printf("ACK Timeout!\r\n");
            #endif
            
            return false;            // 返回NACK状态
        }
        IIC_Delay();
    }
    
    /* 结束检测周期 */
    SCL_SET(0);
    IIC_Delay();
    
    /* 恢复SDA控制（准备后续传输） */
    IIC_SDA_Config(GPIO_Mode_Out_OD);
    
    #ifdef I2C_DEBUG
    USART1_Printf("ACK Received\r\n");
    #endif
    
    return true;                     // 返回ACK状态
}

/**
  * @brief  主机生成应答(ACK)信号
  * @param  None
  * @retval None
  * @note   协议时序要求：
  *         1. 必须在第9个时钟周期内完成
  *         2. SDA低电平必须在SCL高电平期间保持稳定
  *         3. 整个ACK周期需满足标准模式时序
  *         
  *         关键时间参数（标准模式100kHz）：
  *         --------------------------------
  *         | 参数       | 最小值 | 典型值 |
  *         |------------|--------|--------|
  *         | t_ACK_SU   | 0.25us | 4.7us  | (数据建立时间)
  *         | t_ACK_HOLD | 4.0us  | 5.0us  | (应答保持时间)
  *         | t_ACK_LOW  | 4.7us  | 5.0us  | (时钟低电平)
  *         
  * @warning 
  *         1. 仅当主机作为接收方时调用
  *         2. 必须在前8个数据位接收完成后使用
  *         3. SDA低电平必须严格保持到SCL下降沿
  */
void IIC_Ack(void)
{
    /* 确保SDA控制权（配置为开漏输出） */
    IIC_SDA_Config(GPIO_Mode_Out_OD);
    
    /* 阶段1：准备应答信号（SCL低电平期间） */
    SCL_SET(0);                  // 确保时钟线为低
    IIC_Delay();                 // t_ACK_LOW ≥4.7us
    SDA_SET(0);                  // 预置应答信号
    IIC_Delay();                 // t_ACK_SU ≥4.7us
    
    /* 阶段2：产生应答时钟脉冲 */
    SCL_SET(1);                  // 上升沿锁存应答
    IIC_Delay();                 // t_ACK_HOLD ≥4.0us
    
    /* 阶段3：结束应答周期 */
    SCL_SET(0);                  // 结束时钟脉冲
    IIC_Delay();                 // 恢复保持时间
}

/**
  * @brief  主机生成非应答(NACK)信号
  * @param  None
  * @retval None
  * @note   协议关键点：
  *         1. 在第9个时钟周期保持SDA高电平
  *         2. 用于终止数据传输或指示错误状态
  *         3. 时序必须满足标准模式要求
  *         
  *         电气特性（标准模式100kHz）：
  *         --------------------------------
  *         | 参数        | 最小值 | 典型值 |
  *         |-------------|--------|--------|
  *         | t_NACK_SU   | 0.25us | 4.7us  | (建立时间)
  *         | t_NACK_HOLD | 4.0us  | 5.0us  | (保持时间)
  *         | t_NACK_LOW  | 4.7us  | 5.0us  | (时钟低电平)
  *         
  * @warning 
  *         1. 通常在以下场景使用：
  *            - 主机接收的最后一个字节
  *            - 从机数据异常需终止传输
  *         2. 必须在前8个时钟周期后调用
  *         3. SDA高电平必须严格保持完整周期
  */
void IIC_NAck(void)
{
    /* 强制SDA控制权（配置为开漏输出） */
    IIC_SDA_Config(GPIO_Mode_Out_OD);
    
    /* 阶段1：准备非应答信号 */
    SCL_SET(0);                  // 确保时钟线为低
    IIC_Delay();                 // t_NACK_LOW ≥4.7us
    SDA_SET(1);                  // 置位非应答信号
    IIC_Delay();                 // t_NACK_SU ≥4.7us
    
    /* 阶段2：产生时钟脉冲 */
    SCL_SET(1);                  // 上升沿锁存信号
    IIC_Delay();                 // t_NACK_HOLD ≥4.0us
    
    /* 阶段3：结束周期 */
    SCL_SET(0);                  // 结束时钟脉冲
    IIC_Delay();                 // 总线恢复时间
    
    /* 状态标记（可选）*/
    #ifdef I2C_DEBUG
    USART1_Printf("[I2C] NACK Sent\r\n");
    #endif
}

/**
  * @brief  通过I2C总线读取一个字节数据
  * @param  ack: 应答控制标志
  *         @arg 1: 读取后发送应答(ACK)
  *         @arg 0: 读取后发送非应答(NACK)
  * @retval u8: 读取到的数据（0x00-0xFF）
  * @note   时序特性：
  *         1. 数据在SCL上升沿后保持稳定
  *         2. 主机在SCL低电平时读取SDA
  *         3. 遵循MSB(高位优先)传输协议
  *         4. 最后必须发送ACK/NACK完成传输
  * @warning 
  *         1. 调用前需确保I2C总线处于正确状态
  *         2. SDA线必须在函数开始时配置为输入模式
  *         3. 需保证IIC_Delay()提供足够的时序间隔
  * @code
  *     // 典型用法（连续读取时最后一个字节发NACK）
  *     data1 = IIC_Read_Byte(1);  // 读取并应答
  *     data2 = IIC_Read_Byte(0);  // 最后字节发非应答
  * @endcode
  */
u8 IIC_Read_Byte(u8 ack)
{
    u8 byte = 0;
    
    /* 配置SDA为浮空输入（避免影响从机输出） */
    IIC_SDA_Config(GPIO_Mode_IN_FLOATING);
    
    /* 确保SCL初始为低（准备产生时钟脉冲） */
    SCL_SET(0);
    IIC_Delay();

    /* 读取8位数据（MSB First） */
    for(u8 i = 0; i < 8; i++) {
        /* 产生上升沿（从机在此后更新SDA） */
        SCL_SET(1);
        IIC_Delay();  // 等待信号稳定
        
        /* 移位接收（MSB优先） */
        byte <<= 1;
        if(SDA_READ) byte |= 0x01;
        
        /* 产生下降沿（准备下一个bit） */
        SCL_SET(0);
        IIC_Delay();
    }
    
    /* 发送应答/非应答（完成字节传输协议） */
    (ack) ? IIC_Ack() : IIC_NAck();

    return byte;
}

/**
  * @brief  I2C总线数据写入函数
  * @param  dev_addr: 设备地址（7位，范围0x08-0x77）
  * @param  reg_addr: 寄存器地址（8位）
  * @param  data: 待写入数据（8位）
  * @retval bool 
  *         - true:  写入成功
  *         - false: 写入失败（含错误定位）
  * @note   完整协议时序：
  *         ┌───┐ ┌─────────┐ ┌───────┐ ┌─────┐ ┌───┐
  *         │S  │ │DevAddr+W│ │RegAddr│ │Data │ │P  │
  *         └───┘ └─────────┘ └───────┘ └─────┘ └───┘
  *         
  *         错误处理等级：
  *         1级：设备无应答（地址错误/设备离线）
  *         2级：寄存器无应答（寄存器写保护/不存在）
  *         3级：数据无应答（数据格式错误/校验失败）
  *         
  * @warning 
  *         1. 设备地址需左移1位（bit0=0表示写）
  *         2. 两次写入操作需间隔≥5ms（EEPROM类设备）
  *         3. 禁止在中断上下文调用（需保证时序完整性）
  */
bool IIC_Write_Data(u8 dev_addr, u8 reg_addr, u8 data)
{
    /* 参数有效性检查 */
    if(dev_addr != 0x50) {
        #ifdef I2C_DEBUG
        USART1_Printf("[I2C] Invalid Device Address: 0x%02X\r\n", dev_addr);
        #endif
        return false;
    }

    /* 阶段1：启动传输 */
    IIC_Start();
    
    /* 阶段2：发送设备地址（bit0=0表示写） */
    IIC_Send_Byte(dev_addr << 1);
    if(!IIC_Wait_Ack()) {
        #ifdef I2C_DEBUG
        USART1_Printf("[I2C] Device(0x%02X) Not Responding\r\n", dev_addr);
        #endif
        IIC_Stop();
        return false;  // 错误等级1
    }
    
    /* 阶段3：发送寄存器地址 */
    IIC_Send_Byte(reg_addr);
    if(!IIC_Wait_Ack()) {
        #ifdef I2C_DEBUG
        USART1_Printf("[I2C] Register(0x%02X) Access Denied\r\n", reg_addr);
        #endif
        IIC_Stop();
        return false;  // 错误等级2
    }
    
    /* 阶段4：发送数据 */
    IIC_Send_Byte(data);
    if(!IIC_Wait_Ack()) {
        #ifdef I2C_DEBUG
        USART1_Printf("[I2C] Data(0x%02X) Rejected\r\n", data);
        #endif
        IIC_Stop();
        return false;  // 错误等级3
    }
    
    /* 阶段5：终止传输 */
    IIC_Stop();
    IIC_Delay();  // 总线恢复延时
    
    #ifdef I2C_DEBUG
    USART1_Printf("[I2C] Write Success: Dev(0x%02X) Reg(0x%02X)=0x%02X\r\n", 
                 dev_addr, reg_addr, data);
    #endif
    
    return true;
}

/**
  * @brief  I2C总线数据读取函数
  * @param  dev_addr: 设备地址（7位，范围0x08-0x77）
  * @param  reg_addr: 寄存器地址（8位）
  * @retval u8 
  *         - 成功：读取到的数据（0x00-0xFF）
  *         - 失败：0xFF（可通过调试输出定位错误）
  * @note   完整协议时序：
  *         ┌───┐ ┌─────────┐ ┌───────┐ ┌───┐ ┌─────────┐ ┌─────┐ ┌───┐
  *         │S  │ │DevAddr+W│ │RegAddr│ │Sr │ │DevAddr+R│ │Data │ │P  │
  *         └───┘ └─────────┘ └───────┘ └───┘ └─────────┘ └─────┘ └───┘
  *         
  *         错误处理机制：
  *         1. 地址阶段失败：记录设备无应答错误
  *         2. 寄存器阶段失败：记录寄存器访问错误
  *         3. 读取阶段失败：返回0xFF并记录总线错误
  *         
  * @warning 
  *         1. 设备地址需左移1位（bit0表示读写方向）
  *         2. 最后必须发送NACK终止读取
  *         3. 禁止在中断上下文调用（需保证时序完整性）
  */
u8 IIC_Read_Data(u8 dev_addr, u8 reg_addr)
{
    u8 data = 0xFF;  // 默认错误返回值
    
    /* 参数有效性检查 */
    if(dev_addr != 0x50) {
        #ifdef I2C_DEBUG
        USART1_Printf("[I2C] Invalid Device Address: 0x%02X\r\n", dev_addr);
        #endif
        return data;
    }

    /* 阶段1：启动传输并发送寄存器地址 */
    IIC_Start();
    
    /* 阶段2：发送设备地址（写模式） */
    IIC_Send_Byte(dev_addr << 1);
    if(!IIC_Wait_Ack()) {
        #ifdef I2C_DEBUG
        USART1_Printf("[I2C] Device(0x%02X) Not Responding\r\n", dev_addr);
        #endif
        IIC_Stop();
        return data;
    }
    
    /* 阶段3：发送目标寄存器地址 */
    IIC_Send_Byte(reg_addr);
    if(!IIC_Wait_Ack()) {
        #ifdef I2C_DEBUG
        USART1_Printf("[I2C] Register(0x%02X) Access Denied\r\n", reg_addr);
        #endif
        IIC_Stop();
        return data;
    }
    
    /* 阶段4：重复启动条件 */
    IIC_Start();
    
    /* 阶段5：发送设备地址（读模式） */
    IIC_Send_Byte((dev_addr << 1) | 0x01);
    if(!IIC_Wait_Ack()) {
        #ifdef I2C_DEBUG
        USART1_Printf("[I2C] Read Mode Switch Failed\r\n");
        #endif
        IIC_Stop();
        return data;
    }
    
    /* 阶段6：读取数据（最后发送NACK） */
    data = IIC_Read_Byte(0);
    
    /* 阶段7：终止传输 */
    IIC_Stop();
    
    #ifdef I2C_DEBUG
    USART1_Printf("[I2C] Read Success: Dev(0x%02X) Reg(0x%02X)=0x%02X\r\n",
                dev_addr, reg_addr, data);
    #endif
    
    return data;
}

/********************************* 使用示例 ************************************
// 需要用户实现的微秒延时函数
void Delay_us(u32 us)
{
    // 实际实现取决于使用的定时器或系统时钟
}

int main(void)
{
    // 初始化I2C
    IIC_Init();
    
    // 示例：向地址0x50的设备(如EEPROM)的0x00地址写入数据0x55
    IIC_Write_Data(0x50, 0x00, 0x55);
    
    // 示例：从地址0x50的设备(如EEPROM)的0x00地址读取数据
    u8 value = IIC_Read_Data(0x50, 0x00);
    
    while(1)
    {
        // 主循环
    }
}
******************************************************************************/
